/*
 * Copyright © 2017 eqxiu.com 北京中网易企秀科技有限公司  All rights reserved.
 */

package cn.hermit.nio.http.server.rest;

import cn.hermit.data.exception.ParserException;
import cn.hermit.data.json.JSONParser;
import cn.hermit.data.json.model.JSONDocument;
import cn.hermit.data.oson.OSON;
import cn.hermit.nio.http.Http;
import cn.hermit.nio.http.HttpException;
import cn.hermit.nio.http.server.HttpHandler;
import cn.hermit.nio.http.server.HttpRequest;
import cn.hermit.nio.http.server.HttpResponse;
import cn.hermit.util.ReflectionUtils;

import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author JackGao 高建国
 * @description //TODO 设计说明
 * @date 29/5/2017
 */
public class RestfulHandler implements HttpHandler, Http {

    private Method method;

    private Object target;

    public RestfulHandler(Method method, Object target) {
        this.method = method;
        this.target = target;
    }

    @Override
    public void handle(HttpRequest request, HttpResponse response) throws HttpException {
        if (this.method == null) {
            response.addHeader(Http.CONTENT_TYPE, MEDIA_TYPE_TEXT_PLAIN);
            byte[] body = request.getBody() == null ? "Hello Tiny Restful Server".getBytes() : request.getBody();
            response.addHeader(Http.CONTENT_LENGTH, body.length);
            response.setBody(body);
        } else {
            String[] paramNames = ReflectionUtils.getParameterNames(method);
            Class<?>[] parameterTypes = method.getParameterTypes();
            Object[] arguments = new Object[parameterTypes.length];
            Object result = null;
            byte[] body = null;
            try {
                if (method.getDeclaringClass() != target.getClass()) {
                    method = target.getClass().getMethod(method.getName(), method.getParameterTypes());
                }
                if (parameterTypes == null || parameterTypes.length == 0) {
                    result = method.invoke(target, null);
                } else {
                    Type[] genericParameterTypes = method.getGenericParameterTypes();
                    boolean isOnlyBody = false;
                    if (parameterTypes.length == 1) {
                        Class<?> paramType = parameterTypes[0];
                        isOnlyBody = !ReflectionUtils.isBaseType(paramType) && String.class != paramType && Date.class != paramType;
                        if (isOnlyBody) {
                            Type genericType = null;
                            if (genericParameterTypes != null || genericParameterTypes.length > 0) {
                                genericType = genericParameterTypes[0];
                            }
                            try {
                                JSONDocument jsonDocument = JSONParser.getInstance().parse(new InputStreamReader(new ByteArrayInputStream(request.getBody())));
                                Object paramObj = OSON.convertToObject(jsonDocument, paramType, genericType);
                                arguments[0] = paramObj;
                            } catch (ParserException e) {
                                throw new HttpException(e);
                            }
                        }
                    }
                    if (!isOnlyBody) {
                        for (int i = 0; i < paramNames.length; i++) {
                            String paramName = paramNames[i];
                            String val = request.getFormParameter(paramName);
                            Object paramObj = ReflectionUtils.guessObject(val);
                            arguments[i] = paramObj;
                        }
                    }
                    result = method.invoke(target, arguments);
                }
                Class<?> returnType = method.getReturnType();
                String contentType = null;
                if (ReflectionUtils.isBaseType(returnType) || String.class == returnType || Date.class == returnType) {
                    contentType = MEDIA_TYPE_TEXT_PLAIN;
                } else {
                    contentType = MEDIA_TYPE_APPLICATION_JSON;
                }
                if (MEDIA_TYPE_APPLICATION_JSON.equals(contentType)) {
                    if (result == null) {
                        Map map = new HashMap();
                        map.put("result", null);
                        result = map;
                    }
                    body = OSON.convertToJSON(result).toFormatString().getBytes();
                } else {
                    if (result == null) {
                        result = "null";
                    }
                    body = result.toString().getBytes();
                }
                response.addHeader(Http.CONTENT_TYPE, contentType);
                response.addHeader(Http.CONTENT_LENGTH, body.length);
                response.setBody(body);
            } catch (ReflectiveOperationException e) {
                throw new HttpException(e);
            }

        }
    }

}
